// /////////////////////////////////////////////////////////////////////////////
//
// Name:            EntityProcessor.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           AssetProcessor for processing entities in an AssetGroup.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "EntityProcessor.h"
#include "../core/GameManager.h"


// EntityProcessor class
// EntityProcessor constructor.
EntityProcessor::EntityProcessor(AssetGroup *parent)
: AssetProcessor(parent, "entities"), mIsReloading(false)
{
}

// EntityProcessor deconstructor.
EntityProcessor::~EntityProcessor()
{
    removeAssets();
}

// Gets the entity with the given filename.
Entity* EntityProcessor::getEntity(const std::string &fileName)
{
    // Did we already instanciate the entity?
#ifdef WIN32
	std::string tmp = fileName;
	std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
	std::map<std::string, Entity*>::iterator itEntities = mEntities.find(tmp);
#else
    std::map<std::string, Entity*>::iterator itEntities = mEntities.find(fileName);
#endif

    if(itEntities != mEntities.end())
      return itEntities->second;

    // Do we have the file in memory so that we can instanciate the entity from there?
#ifdef WIN32
	std::map<std::string, std::pair<c8*, long> >::iterator itFiles = mEntityFiles.find(tmp);
#else
    std::map<std::string, std::pair<c8*, long> >::iterator itFiles = mEntityFiles.find(fileName);
#endif

    if(itFiles != mEntityFiles.end())
    {
        // Retrieve pointers to sub-sytems of the Irrlicht engine.
        IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

        // Create a memory read file.
        long fSize = (itFiles->second).second;
        c8 *fBuffer = (itFiles->second).first;

        IReadFile *file = fileSystem->createMemoryReadFile(fBuffer, fSize, fileName.c_str());
        IXMLReader *xml = fileSystem->createXMLReader(file);

        // Create the entity.
        Entity *entity = GameManager::Instance()->getEntityManager()
                           ->createEntityFromXML(xml, NULL, pParent, false);

        mEntities[fileName] = entity;
        entity->grab();

        // Clean up.
        xml->drop();
        file->drop();

        // Return the entity.
        return entity;
    }

    // We didn't find anything.
    return NULL;
}

// Gets a entity tree with the given entity as root.
void EntityProcessor::getEntityTree(Entity *root)
{
    // Iterate through all entity files.
    std::map<std::string, std::pair<c8*, long> >::iterator itFiles;

    for(itFiles = mEntityFiles.begin(); itFiles != mEntityFiles.end(); itFiles++)
    {
        // Get a pointer to the entity.
        Entity *entity = NULL;

        // Did we already instanciate the entity?
        std::map<std::string, Entity*>::iterator itEntities = mEntities.find(itFiles->first);

        if(itEntities != mEntities.end())
          entity = itEntities->second;

        // Otherwise we'll have to instanciate the entity.
        else
        {
           // Retrieve pointers to sub-sytems of the Irrlicht engine.
           IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

           // Create a memory read file.
           long fSize = (itFiles->second).second;
           c8 *fBuffer = (itFiles->second).first;

           IReadFile *file = fileSystem->createMemoryReadFile(fBuffer, fSize, (itFiles->first).c_str());
           IXMLReader *xml = fileSystem->createXMLReader(file);

           // Create the entity.
           Entity *entity = GameManager::Instance()->getEntityManager()
                              ->createEntityFromXML(xml, NULL, pParent, false);

           mEntities[itFiles->first] = entity;
           entity->grab();

           // Clean up.
           xml->drop();
           file->drop();
        }

        // Check if we should add the entity as a child.
        if(entity != NULL)
        {
           if(entity->getParent() == NULL)
             entity->setParent(root);
        }
    }
}

// Loads the mesh with the given filename.
bool EntityProcessor::loadAsset(const std::string &fileName)
{
    // Retrieve pointers to sub-sytems of the Irrlicht engine.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Change the working directory to the entities directory.
    std::string workingDir = (fileSystem->getWorkingDirectory()).c_str();

    if(!fileSystem->changeWorkingDirectoryTo( (pParent->getBaseDir()).c_str() ) )
      return false;

    if(!fileSystem->changeWorkingDirectoryTo("./entities/"))
      return false;

    // Load the entity.
    IReadFile *file = fileSystem->createAndOpenFile(fileName.c_str());

    if(file)
    {
       // Retrieve data.
       long fSize = file->getSize();
       c8 *fBuffer = new c8[fSize+1];
       memset(fBuffer, 0, fSize+1);

       file->read(fBuffer, fSize);

       // Store data.
       mEntityFiles[fileName] = std::pair<c8*, long>(fBuffer, fSize);
       cout << "Loaded entity: " << fileName << "\n";

       // Handle reload.
       if(mIsReloading)
       {
#ifdef WIN32
		   std::string tmp = fileName;
		   std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
           std::map<std::string, Entity*>::iterator it = mEntities.find(tmp);
#else
           std::map<std::string, Entity*>::iterator it = mEntities.find(fileName);
#endif

           if(it != mEntities.end())
           {
               file->seek(0);
               IXMLReader *xml = fileSystem->createXMLReader(file);

               Entity *entity = it->second;

               entity->removeComponents();

               if(entity->getParent() != NULL)
               {
                  entity->setAbsolutePosition(entity->getParent()->getAbsolutePosition());
                  entity->setAbsoluteRotation(entity->getParent()->getAbsoluteRotation());
               }

               entity->loadXML(xml);

               xml->drop();
           }
       }

       // Clean up.
       file->drop();
    }

    // Clean up.
    fileSystem->changeWorkingDirectoryTo(workingDir.c_str());

    return (file) ? true : false;
}

// Loads the meshes associated with this asset group.
void EntityProcessor::loadAssets()
{
    // Retrieve pointers to sub-sytems of the Irrlicht engine.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Change the working directory to the entities directory.
    std::string workingDir = (fileSystem->getWorkingDirectory()).c_str();

    if(!fileSystem->changeWorkingDirectoryTo( (pParent->getBaseDir()).c_str() ) )
      return;

    if(!fileSystem->changeWorkingDirectoryTo("./entities/"))
      return;

    // Read the entities.
    IFileList *fileList = fileSystem->createFileList();

    for(u32 i = 0; i < fileList->getFileCount(); i++)
    {
        // Get file name.
        std::string fileName = (fileList->getFileName(i)).c_str();

        if(fileName == "..")
          continue;

        // Load the entities.
        IReadFile *file = fileSystem->createAndOpenFile(fileName.c_str());

        if(file)
        {
           // Retrieve data.
           long fSize = file->getSize();
           c8 *fBuffer = new c8[fSize+1];
           memset(fBuffer, 0, fSize+1);

           file->read(fBuffer, fSize);

           // Store data.
           mEntityFiles[fileName] = std::pair<c8*, long>(fBuffer, fSize);
           cout << "Loaded entity: " << fileName << "\n";

           // Handle reload.
           if(mIsReloading)
           {
#ifdef WIN32
			  std::string tmp = fileName;
			  std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
              std::map<std::string, Entity*>::iterator it = mEntities.find(tmp);
#else
              std::map<std::string, Entity*>::iterator it = mEntities.find(fileName);
#endif

              if(it != mEntities.end())
              {
                 file->seek(0);
                 IXMLReader *xml = fileSystem->createXMLReader(file);

                 Entity *entity = it->second;

                 entity->removeComponents();

                 if(entity->getParent() != NULL)
                 {
                    entity->setAbsolutePosition(entity->getParent()->getAbsolutePosition());
                    entity->setAbsoluteRotation(entity->getParent()->getAbsoluteRotation());
                 }

                 entity->loadXML(xml);

                 xml->drop();
              }
           }

           // Clean up.
           file->drop();
        }
    }

    // Clean up.
    fileList->drop();

    fileSystem->changeWorkingDirectoryTo(workingDir.c_str());
}

// Reloads the asset with the given filename.
bool EntityProcessor::reloadAsset(const std::string &fileName)
{
    mIsReloading = true;

    removeAsset(fileName);
    bool succeeded = loadAsset(fileName);

    mIsReloading = false;

    return succeeded;
}

// Reloads all assets.
void EntityProcessor::reloadAssets()
{
    // We do a regular reload.
    mIsReloading = true;

    removeAssets();
    loadAssets();

    mIsReloading = false;

    // We need to resync our entities map.
    // Create a new map with all existing entities.
    std::map<std::string, Entity*> newMap;

    std::map<std::string, std::pair<c8*, long> >::iterator itFiles;
    for(itFiles = mEntityFiles.begin(); itFiles != mEntityFiles.end(); itFiles++)
    {
        std::map<std::string, Entity*>::iterator itEntities = mEntities.find(itFiles->first);

        if(itEntities != mEntities.end())
        {
            newMap[itEntities->first] = itEntities->second;
            mEntities.erase(itEntities);
        }
    }

    // Remove redundant entities.
    std::map<std::string, Entity*>::iterator itEntities;
    for(itEntities = mEntities.begin(); itEntities != mEntities.end(); itEntities++)
       (itEntities->second)->drop();

    mEntities.clear();

    // Set new map.
    mEntities = newMap;
}

// Removes the asset with the given filename.
bool EntityProcessor::removeAsset(const std::string &fileName)
{
    // Predeclaration of succeeded variable.
    bool succeeded = false;

    // Try to find the given entity file.
#ifdef WIN32
	std::string tmp = fileName;
	std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
	std::map<std::string, std::pair<c8*, long> >::iterator it = mEntityFiles.find(fileName);
#else
    std::map<std::string, std::pair<c8*, long> >::iterator it = mEntityFiles.find(fileName);
#endif

    // Did we find the entity file?
    if(it != mEntityFiles.end())
    {
       // Delete the entity file.
       c8 *fBuffer = (it->second).first;
       delete [] fBuffer;
       mEntityFiles.erase(it);

       succeeded = true;
    }

    // Try to find the given entity, but only if we're not reloading.
    if(succeeded && !mIsReloading)
    {
#ifdef WIN32
       std::map<std::string, Entity*>::iterator it = mEntities.find(tmp);
#else
       std::map<std::string, Entity*>::iterator it = mEntities.find(fileName);
#endif

       // Did we find the entity?
       if(it != mEntities.end())
       {
          (it->second)->drop();
          mEntities.erase(it);
       }
    }

    // Return whether we succeeded or not.
    return succeeded;
}

// Removes all asssets.
void EntityProcessor::removeAssets()
{
    // Delete the entity files.
    std::map<std::string, std::pair<c8*, long> >::iterator itFiles;

    for(itFiles = mEntityFiles.begin(); itFiles != mEntityFiles.end(); itFiles++)
    {
       // Delete the entity file.
       c8 *fBuffer = (itFiles->second).first;
       delete [] fBuffer;
    }

    // Clear the entity files map.
    mEntityFiles.clear();

    // Remove the entities, but only if we're not reloading.
    if(!mIsReloading)
    {
       std::map<std::string, Entity*>::iterator itEntities;

       for(itEntities = mEntities.begin(); itEntities != mEntities.end(); itEntities++)
         (itEntities->second)->drop();

       // Clear the entities map.
       mEntities.clear();
    }
}

// Creates a new EntityProcessor.
AssetProcessor* EntityProcessor::createProcessor(AssetGroup *parent)
{
    return new EntityProcessor(parent);
}

// End of File
